package middleware

import (
	"bytes"
	"github.com/gin-gonic/gin"
	"github.com/mssola/user_agent"
	"go.uber.org/zap"
	"io/ioutil"
	"net/http"
	"perfect-vue-admin/global"
	"perfect-vue-admin/model"
	"perfect-vue-admin/model/request"
	"perfect-vue-admin/service"
	"perfect-vue-admin/utils"
	"strconv"
	"time"
)

func OperationRecord() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 为防止请求日志过多 暂时只记录非GET请求
		if c.Request.Method == http.MethodGet {
			c.Next()
			return
		}

		var body []byte
		var username string
		if c.Request.Method != http.MethodGet {
			var err error
			body, err = ioutil.ReadAll(c.Request.Body)
			if err != nil {
				global.LOG.Error("read body from request error:", zap.Any("err", err))
			} else {
				c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
			}
		}
		if claims, ok := c.Get("claims"); ok{
			user := claims.(*request.CustomClaims)
			username = user.Username
		}else {
			id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
			if err == nil {
				_, user := service.GetUserById(uint(id))
				username = user.Username
			}
		}
		ip := c.ClientIP()
		path := c.Request.URL.Path
		err, desc := service.GetApiDescriptionByPath(path, c.Request.Method)
		if err != nil{
			desc = "api " + path + " 数据库中不存在"
		}
		agent := c.Request.UserAgent()
		ua := user_agent.New(c.Request.UserAgent())
		browserName, browserVersion := ua.Browser()

		record := model.SysOperationRecord{
			Ip:       ip,
			Desc:     desc,
			Location: utils.GetLocation(ip),
			Method:   c.Request.Method,
			Path:     path,
			Agent:    agent,
			Body:     string(body),
			Username: username,
			Browser:  browserName + " " + browserVersion,
			Os:       ua.OS(),
			Platform: ua.Platform(),
			Status: c.Writer.Status(),
		}

		writer := ResponseBodyWriter{
			ResponseWriter: c.Writer,
			Body:           &bytes.Buffer{},
		}
		c.Writer = writer

		now := time.Now()

		c.Next()

		latency := time.Now().Sub(now).String()
		record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
		record.Status = c.Writer.Status()
		record.Latency = latency
		record.Resp = writer.Body.String()


		if err := service.CreateSysOperationRecord(record); err != nil {
			global.LOG.Error("create operation record error:", zap.Any("err", err))
		}
	}
}

type ResponseBodyWriter struct {
	gin.ResponseWriter
	Body *bytes.Buffer
}

func (r ResponseBodyWriter) Write(b []byte) (int, error) {
	r.Body.Write(b)
	return r.ResponseWriter.Write(b)
}
